package com.liuya.common;

import java.util.Map;

import com.liuya.common.support.security.coder.DESCoder;
import com.liuya.common.support.security.coder.MDCoder;
import com.liuya.common.support.security.BASE64Encoder;
import com.liuya.common.support.security.coder.HmacCoder;
import com.liuya.common.support.security.coder.RSACoder;
import com.liuya.common.support.security.coder.SHACoder;
import java.security.MessageDigest;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.util.Assert;

/**
 * 数据加密辅助类(默认编码UTF-8)
 *
 * @author liuya
 * @version 0.1.0
 */
public final class SecurityUtil {
    private SecurityUtil() {
    }

    /**
     * 默认算法密钥
     */
    private static final byte[] ENCRYPT_KEY = {-81, 0, 105, 7, -32, 26, -49, 88};
    private static final String PRIVATE_KEY = "privateKey";
    public static final String CHARSET = "UTF-8";
    private static final Provider privider = new BouncyCastleProvider();

    /**
     * BASE64编码
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static final String encryptBASE64(byte[] key) {
        try {
            return new BASE64Encoder().encode(key);
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
    }

    /**
     * BASE64解码
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static final byte[] decryptBASE64(String key) {
        try {
            return new BASE64Encoder().decode(key);
        } catch (Exception e) {
            throw new RuntimeException("解密错误，错误信息：", e);
        }
    }

    /**
     * 数据加密，算法（DES）
     *
     * @param data 要进行加密的数据
     * @return 加密后的数据
     */
    public static final String encryptDes(String data) {
        return encryptDes(data, ENCRYPT_KEY);
    }

    /**
     * 数据加密，算法（DES）
     *
     * @param data 要进行加密的数据
     * @return 加密后的数据
     */
    public static final String encryptDes(String data, byte[] key) {
        String encryptedData = null;
        try {
            // 加密，并把字节数组编码成字符串
            encryptedData = encryptBASE64(DESCoder.encrypt(data.getBytes(), key));
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
        return encryptedData;
    }

    /**
     * 数据解密，算法（DES）
     *
     * @param cryptData 加密数据
     * @return 解密后的数据
     */
    public static final String decryptDes(String cryptData) {
        return decryptDes(cryptData, ENCRYPT_KEY);
    }

    /**
     * 数据解密，算法（DES）
     *
     * @param cryptData 加密数据
     * @return 解密后的数据
     */
    public static final String decryptDes(String cryptData, byte[] key) {
        String decryptedData = null;
        try {
            // 把字符串解码为字节数组，并解密
            decryptedData = new String(DESCoder.decrypt(decryptBASE64(cryptData), key));
        } catch (Exception e) {
            throw new RuntimeException("解密错误，错误信息：", e);
        }
        return decryptedData;
    }

    /**
     * 基于MD5算法的单向加密
     *
     * @param strSrc 明文
     * @return 返回密文
     */
    public static final String encryptMd5(String strSrc) {
        String outString = null;
        try {
            outString = encryptBASE64(MDCoder.encodeMD5(strSrc.getBytes(CHARSET)));
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
        return outString;
    }

    /**
     * 使用MD5返回32位长度的字符串
     * @param data
     * @return
     */
    public static String encryptMD5Hex(String data) {
        return DigestUtils.md5Hex(data);
    }

    public static String encryptMd5Custom(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            str = buf.toString();
        } catch (Exception e) {
            e.printStackTrace();

        }
        return str;
    }

    /**
     * SHA加密
     *
     * @param data
     * @return
     * @throws Exception
     */
    public static final String encryptSHA(String data) {
        try {
            return encryptBASE64(SHACoder.encodeSHA256(data.getBytes(CHARSET)));
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
    }

    /**
     * HMAC加密
     *
     * @param data
     * @return
     * @throws Exception
     */
    public static final String encryptHMAC(String data) {
        return encryptHMAC(data, ENCRYPT_KEY);
    }

    /**
     * HMAC加密
     *
     * @param data
     * @return
     * @throws Exception
     */
    public static final String encryptHMAC(String data, byte[] key) {
        try {
            return encryptBASE64(HmacCoder.encodeHmacSHA512(data.getBytes(CHARSET), key));
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
    }

    /**
     * RSA签名
     *
     * @param data 原数据
     * @return
     */
    public static final String signRSA(String data, String privateKey) {
        try {
            return encryptBASE64(RSACoder.sign(data.getBytes(CHARSET), decryptBASE64(privateKey)));
        } catch (Exception e) {
            throw new RuntimeException("签名错误，错误信息：", e);
        }
    }

    /**
     * RSA验签
     *
     * @param data 原数据
     * @return
     */
    public static final boolean verifyRSA(String data, String publicKey, String sign) {
        try {
            return RSACoder.verify(data.getBytes(CHARSET), decryptBASE64(publicKey), decryptBASE64(sign));
        } catch (Exception e) {
            throw new RuntimeException("验签错误，错误信息：", e);
        }
    }

    /**
     * 数据加密，算法（RSA）
     *
     * @param data 数据
     * @return 加密后的数据
     */
    public static final String encryptRSAPrivate(String data, String privateKey) {
        try {
            return encryptBASE64(RSACoder.encryptByPrivateKey(data.getBytes(CHARSET), decryptBASE64(privateKey)));
        } catch (Exception e) {
            throw new RuntimeException("解密错误，错误信息：", e);
        }
    }

    /**
     * 数据解密，算法（RSA）
     *
     * @param cryptData 加密数据
     * @return 解密后的数据
     */
    public static final String decryptRSAPublic(String cryptData, String publicKey) {
        try {
            // 把字符串解码为字节数组，并解密
            return new String(RSACoder.decryptByPublicKey(decryptBASE64(cryptData), decryptBASE64(publicKey)));
        } catch (Exception e) {
            throw new RuntimeException("解密错误，错误信息：", e);
        }
    }

    public static KeyPair generateKeyPair() {
        try {
            KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", privider);
            gen.initialize(1024, new SecureRandom());
            return gen.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密 RSA
     * @param publicKey
     * @param data
     * @return
     */
    public static byte[] encryptRsa(PublicKey publicKey, byte[] data) {
        Assert.notNull(publicKey);
        Assert.notNull(data);
        try {
            Cipher cipher = Cipher.getInstance("RSA", privider);
            cipher.init(1, publicKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密 RSA
     * @param privateKey
     * @param data
     * @return
     */
    public static byte[] decryptRsa(PrivateKey privateKey, byte[] data) {
        Assert.notNull(privateKey);
        Assert.notNull(data);
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding",privider);
            cipher.init(2, privateKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 加密 RSA
     * @param publicKey
     * @param text
     * @return
     */
    public static String encryptRsa(PublicKey publicKey, String text) {
        Assert.notNull(publicKey);
        Assert.notNull(text);
        byte[] strs = encryptRsa(publicKey, text.getBytes());
        return strs != null ? Base64.encodeBase64String(strs) : null;
    }

    /**
     * 解密 RSA
     * @param privateKey
     * @param text
     * @return
     */
    public static String decryptRsa(PrivateKey privateKey, String text) {
        Assert.notNull(privateKey);
        Assert.notNull(text);
        byte[] strs = decryptRsa(privateKey, Base64.decodeBase64(text));
        return strs != null ? new String(strs) : null;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(encryptDes("SHJR"));
        System.out.println(decryptDes("INzvw/3Qc4q="));
        System.out.println(encryptMd5("SHJR"));
        System.out.println(encryptSHA("1"));
        Map<String, Object> key = RSACoder.initKey();
        String privateKey = encryptBASE64(RSACoder.getPrivateKey(key));
        String publicKey = encryptBASE64(RSACoder.getPublicKey(key));
        System.out.println(privateKey);
        System.out.println(publicKey);
        String sign = signRSA("132", privateKey);
        System.out.println(sign);
        String encrypt = encryptRSAPrivate("132", privateKey);
        System.out.println(encrypt);
        String org = decryptRSAPublic(encrypt, publicKey);
        System.out.println(org);
        System.out.println(verifyRSA(org, publicKey, sign));

        // System.out.println("-------列出加密服务提供者-----");
        // Provider[] pro = Security.getProviders();
        // for (Provider p : pro) {
        // System.out.println("Provider:" + p.getName() + " - version:" +
        // p.getVersion());
        // System.out.println(p.getInfo());
        // }
        // System.out.println("");
        // System.out.println("-------列出系统支持的消息摘要算法：");
        // for (String s : Security.getAlgorithms("MessageDigest")) {
        // System.out.println(s);
        // }
        // System.out.println("-------列出系统支持的生成公钥和私钥对的算法：");
        // for (String s : Security.getAlgorithms("KeyPairGenerator")) {
        // System.out.println(s);
        // }
    }
}
